package emarket;

// Environment code for project EMarket

import jason.asSyntax.*;
import jason.environment.*;
import jason.stdlib.literal;


import java.lang.StringBuilder;
import java.util.logging.*;
import java.util.*;

public class Emarket extends Environment {

	private static final String MAINTAINER = "maintainer";
	
    private Logger logger = Logger.getLogger("EMarket."+Emarket.class.getName());
    
    
    private List<Offer> 	_offers = new ArrayList<Offer>();
    private List<Request>	_requests = new ArrayList<Request>();
    
   
    
    

    /** Called before the MAS execution with the args informed in .mas2j */
    @Override
    public void init(String[] args) {
        super.init(args);
        addPercept(Literal.parseLiteral("percept(demo)"));
    }

    
    public boolean isAllowedToPerformFunction(String agName, String functor)
    {
    	if(agName.equals("maintainer"))
    		return true;
    	
    	
    	return false;
    }
    
    @Override
    public boolean executeAction(String agName, Structure action) 
    {   	
    	String functor = action.getFunctor();
    	if(!isAllowedToPerformFunction(agName, functor))
    		return false;
    	
    	if( functor.equals( "registerOffer" ) )
    	{
    		return registerOffer(
        			action.getTerm(0).toString(),
        			action.getTerm(1).toString(),
        			agName
        			
    		);
    	}
    	else if(functor.equals("registerRequest"))
    	{
    		return this.registerRequest(
    				action.getTerm(0).toString(),
    				action.getTerm(1).toString(),
    				agName
    				);
    		
    	}
    	else if(functor.equals("findMatchingOffers"))
    	{
    		return this.findMatchingOffers(
    					action.getTerm(0).toString(),
    					action.getTerm(1).toString(),
    					agName
    					);
    	}
    	else if(functor.equals("removeOffer"))
    	{
    		
    		return this.removeOffer(
    				action.getTerm(0).toString(),
    				action.getTerm(1).toString(),
    				agName
    				);
    		
    	}
    	else if(functor.equals("removeRequest"))
    	{
    		return this.removeRequest(
    				action.getTerm(0).toString(),
    				action.getTerm(1).toString()
    				);
    	}
    	else
    	{
    		logger.info("executing: "+action+", but not implemented!");
    		return false;
    	}
    }

    /** Called before the end of MAS execution */
    @Override
    public void stop() {
        super.stop();
    }
    
	/**
	 * Register a new request in the environment, a trader can only have 1 request.
	 * If the trader is allready offering the item, the method fails.
	 * @param itemName Name of the item
	 * @param buyer Name of the requesting Trader
	 * @return A boolean indicating whether this method was successful.
	 */
	private boolean registerRequest( String itemName, String buyer, String maintainerName )
	{
		Request request = new Request( itemName, buyer  );
		
		boolean requestAdded = false;
		 //If the trader is also offering the item, the method fails.
		if(_offers.contains(new Offer(itemName, buyer)))
		{
			requestAdded = false;
		} 
		// If the request is not allready registered, register the item.
		if(!_requests.contains(request))
		{
			 requestAdded = this._requests.add(request);
		}
	
			
		return requestAdded;
	}
    
    /**
	 * Register a new offer in the environment.
	 * @param seller Name of the offering Trader
	 * @param itemName Name of the item
	 * @return A boolean indicating whether this method was succesful.
	 */
	public boolean registerOffer( String itemName, String seller, String maintainerName )
	{
		Offer offer = new Offer( itemName, seller );
		
		boolean offerAdded = false;
		
		//If the trader is also requesting this item, the method fails.
		if(_requests.contains(new Request(itemName, seller)))
		{
			offerAdded = false;
		}
		//Otherwise the offer is added to the database.
		else
		{
			offerAdded = _offers.add(offer);
		}
		
		if(offerAdded)
		{
			
			Set<Request> matchingRequests = getMatchingRequests(itemName);
			if(matchingRequests.size() > 0)
			{
				
				String oldPercept = "buyersWhereOfferAdded(" + seller + "," + itemName + ", _)";
		    	StringBuilder newPercept = new StringBuilder();
				for(Request request : matchingRequests)
		    	{
					newPercept.append(request.get_trader().get_identifier() + ",");
		    	}
				
				
			 	//remove last comma.
		    	if(newPercept.length() > 0)
		    		newPercept.delete(newPercept.length()-1, newPercept.length());
		    	//insert this before the list.
		    	newPercept.insert(0, "buyersWhereOfferAdded(" + seller + "," + itemName + ", [");
		    	newPercept.append("])");
				
				
		    	removePerceptsByUnif(maintainerName, Literal.parseLiteral(oldPercept));
		    	addPercept( maintainerName, Literal.parseLiteral( newPercept.toString() ) );
		
			}
		}
	
		
		return offerAdded;
		
	}
	
	
	
	/**
	 * Removes an existing request
	 * @param itemName The Name of the item
	 * @param buyer The name of the buyer.
	 * @return A boolean indicating wether the request database no longer contains the request.
	 */
	public boolean removeRequest(String itemName, String buyer)
	{
		Request request = new Request(itemName, buyer);
		
		if(_requests.contains(request))
		{
			return _requests.remove(request);
		}
		
		//Return true if the request didnt exists.
		return true;
	}
	
	/**
	 * Removes an existing offer
	 * @param itemName The name of the item.
	 * @param seller The name of the seller.
	 * @return A boolean indicating whether the offer databsae no longer contains the offer.
	 */
	public boolean removeOffer(String itemName, String seller, String maintainerName)
	{
		Offer offer = new Offer(itemName, seller);
		
		boolean offerRemoved = false;
		boolean dbNotContainsOffer = true;
		
		if(_offers.contains(offer))
		{
			offerRemoved = _offers.remove(offer);
			dbNotContainsOffer = offerRemoved;
		}
		
		if(offerRemoved)
		{
			
			Set<Request> matchingRequests = getMatchingRequests(itemName);
			if(matchingRequests.size() > 0)
			{
				
				String oldPercept = "buyersWhereOfferRemoved(" + seller + "," + itemName + ", _)";
		    	StringBuilder newPercept = new StringBuilder();
				for(Request request : matchingRequests)
		    	{
					newPercept.append(request.get_trader().get_identifier() + ",");
		    	}
				
				
			 	//remove last comma.
		    	if(newPercept.length() > 0)
		    		newPercept.delete(newPercept.length()-1, newPercept.length());
		    	//insert this before the list.
		    	newPercept.insert(0, "buyersWhereOfferRemoved(" + seller + "," + itemName + ", [");
		    	newPercept.append("])");
				
				
		    	removePerceptsByUnif(maintainerName, Literal.parseLiteral(oldPercept));
		    	addPercept( maintainerName, Literal.parseLiteral( newPercept.toString() ) );
		
			}
		}
		
		//Return true if the offer didnt exists/
		return dbNotContainsOffer;
	}
	
	
	public boolean findMatchingOffers(String itemName, String buyer, String maintainerName)
	{
		Set<Offer> matchingOffers = getMatchingOffers(itemName);
		
		String oldPercept = "matchingOffers(" + buyer + "," + itemName + ", _)";
		StringBuilder newPercept = new StringBuilder();
		for(Offer offer : matchingOffers)
		{
			newPercept.append(offer.get_trader().get_identifier() + ",");
		}
								
		//remove last comma.
		if(newPercept.length() > 0)
		   	newPercept.delete(newPercept.length()-1, newPercept.length());
		//insert this before the list.
		newPercept.insert(0, "matchingOffers(" + buyer + "," + itemName + ", [");
		newPercept.append("])");
			
			
		removePerceptsByUnif(maintainerName, Literal.parseLiteral(oldPercept));
		addPercept( maintainerName, Literal.parseLiteral( newPercept.toString() ) );
		
		return true;
	}
	
	
    /**
     * Finds offers that match the given item.
     * @param itemName
     * @param buyer
     * @param maintainerName
     * @return A boolean indicating whether this method was succesful.
     */
    private Set<Offer> getMatchingOffers(String itemName)
    {
    	Set<Offer> matchingOffers = new HashSet<Offer>();
    	Item item = new Item(itemName);
    	
    	for(Offer offer : _offers)
    	{
    		if(offer.get_item().equals(item))
    		{
    			matchingOffers.add(offer);
    		}
    	}	
    	return matchingOffers;
    }
    
    private Set<Request> getMatchingRequests(String itemName)
    {
    	Set<Request> matchingRequests = new HashSet<Request>();
    	Item item = new Item(itemName);
    	
    	for(Request request : _requests)
    	{
    		if(request.get_item().equals(item))
    		{
    			matchingRequests.add(request);
    		}
    	}
        	
    	return matchingRequests;
    }
    
}

